package csm.sql;

import java.io.Reader;
import java.io.StringReader;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import csm.common.StringUtil;

/**
 * @author seungmun
 * @date 2006. 6. 23
 */
public class XMLResultSet
{
    final static int BUFFER_SIZE = 1024 * 400;
    //static int       index;
    private String   xmlString;

    /**
     * 생성자
     * @param rs
     * @throws SQLException
     */
    public XMLResultSet(ResultSet rs) throws SQLException
    {
        this.xmlString = XMLUtil.getResultSet2XMLString(rs);
    }

    /**
     * 현재 ResultSet을 XML 문자열로 가져옵니다.
     * @return
     */
    public String getXML()
    {
        return this.xmlString;
    }

    /**
     * 현재 ResultSet을 List로 반환합니다.
     * @return HashMap <column Name, column Value> 형식이 저장된 List
     * @throws Exception
     */
    public List<HashMap<String, String>> getList() throws Exception
    {
        return XMLUtil.getXML2List(this.xmlString);
    }

    private static class XMLUtil
    {
        /**
         * DB의 Data를 ResultSet을 넘겨서 XML String으로 변환
         */
        public static String getResultSet2XMLString(ResultSet rs) throws SQLException
        {
            final StringBuffer buffer = new StringBuffer(BUFFER_SIZE);
            int colCount = 0;

            if (rs == null)
                return "";

            if (!rs.next())
                return "";

            ResultSetMetaData rsmd = rs.getMetaData();

            colCount = rsmd.getColumnCount();

            if (colCount > 0)
            {
                buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
                buffer.append("<DATASET>\n");
            }

            int type;
            while (rs.next())
            {
                buffer.append("\t<DATAROW>\n");
                for (int i = 1; i <= colCount; i++)
                {
                    type = rsmd.getColumnType(i);
                    buffer.append("\t\t<" + rsmd.getColumnName(i) + ">");
                    buffer.append(getValue(rs, i, type));
                    buffer.append("</" + rsmd.getColumnName(i) + ">\n");
                }
                buffer.append("\t</DATAROW>\n");
            }

            if (colCount > 0)
            {
                buffer.append("</DATASET>");
            }

            return buffer.toString();
        }

        /**
         * @param rs
         * @param colNum
         * @param type
         * @return
         * @throws SQLException
         */
        private static String getValue(final ResultSet rs, int colNum, int type) throws SQLException
        {
            switch (type)
            {
                case Types.ARRAY:
                case Types.BLOB:
                case Types.CLOB:
                case Types.DISTINCT:
                case Types.LONGVARBINARY:
                case Types.VARBINARY:
                case Types.BINARY:
                case Types.REF:
                case Types.STRUCT:
                    return "undefined";

                default:
                {
                    Object value = rs.getObject(colNum);
                    if (rs.wasNull() || (value == null))
                        return ("null");
                    else
                        return (value.toString());
                }
            }
        }

        /**
         * XML Documnet Load to ArrayList
         * 
         * @param s
         * @return HashMap <NodeName, NodeValue> 형식이 저장된 List
         * @throws Exception
         */
        public static List<HashMap<String, String>> getXML2List(String s) throws Exception
        {
            Element el = loadDocument(s);
            return getNodeValues(el);
        }

        /**
         * XML Documnet Load
         */
        private static Element loadDocument(String s) throws Exception
        {
            try
            {
                Reader in = new StringReader(s);
                InputSource inputsource = new InputSource(in);
                DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
                Document document = docBuilder.parse(inputsource);
                Element element = document.getDocumentElement();
                element.normalize();

                return element;
            }
            catch (Exception ee)
            {
                System.out.println("loadDocument Exception : " + ee.getMessage());
                ee.printStackTrace(System.err);
                throw ee;
            }
        }

        /**
         * @param root
         * @return HashMap <NodeName, NodeValue> 형식이 저장된 List
         */
        private static List<HashMap<String, String>> getNodeValues(Element root)
        {
            List<HashMap<String, String>> listXML = new ArrayList<HashMap<String, String>>();

            NodeList parentNotes = root.getChildNodes();
            for (int i = 0; i < parentNotes.getLength(); i++)
            {
                Node node = (Node) parentNotes.item(i);
                if (node.getNodeType() == Element.ELEMENT_NODE)
                {
                    if (node.getFirstChild() != null)
                    {
                        listXML.add(getSubNodeValue(node));
                    }
                }
            }

            return listXML;
        }

        /**
         * 지정한 노드의 내용을 HashMap에 저장하여 반환합니다. Child Node에 값이 있을때까지 찾는다...
         * 
         * @param node
         * @return HashMap <NodeName, NodeValue> 형식
         */
        private static HashMap<String, String> getSubNodeValue(Node node)
        {
            HashMap<String, String> map = new HashMap<String, String>();
            NodeList childNotes = node.getChildNodes();
            for (int j = 0; j < childNotes.getLength(); j++)
            {
                Node childNode = (Node) childNotes.item(j);
                if (childNode.getNodeType() == Element.ELEMENT_NODE)
                {
                    String key = childNode.getNodeName();
                    if (childNode.getFirstChild() != null)
                    {
                        String value = childNode.getFirstChild().getNodeValue();
                        if (StringUtil.isNullOrEmpty(value))
                            map = getSubNodeValue(childNode);
                        else
                            map.put(key, value);
                    }
                }
            }
            return map;
        }

    }
}
